package handler

import (
	"context"
	"fmt"
	"gitee.com/bi_ygg/assemblies/grpcPool"
	"gitee.com/bi_ygg/assemblies/internal"
	"github.com/gofiber/fiber/v2"
	"github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	gResolver "google.golang.org/grpc/resolver"
)

type gRPCResp[D any] interface {
	GetStatus() uint32
	GetMsg() string
	GetData() D
}

type BindReqFunc[R any] func(c *fiber.Ctx, req *R) error

func DefaultBindReqFunc[R any](c *fiber.Ctx, req *R) error {
	if err := c.BodyParser(req); err != nil {
		logrus.Warnf("body parse err: %v", err)
		return internal.Resp400(c)
	}

	return nil
}

func DefaultQueryReqFunc[R any](c *fiber.Ctx, req *R) error {
	if err := c.QueryParser(req); err != nil {
		logrus.Warnf("query parse err: %v", err)
		return internal.Resp400(c)
	}

	return nil
}

func NewFiberHandler[R any, RR any, D any](
	resolver gResolver.Builder,
	project, moduleType string, moduleName string,
	ctx context.Context,
	service *grpc.ServiceDesc,
	method interface{},
	bindReqFn ...BindReqFunc[R],
) fiber.Handler {
	return func(c *fiber.Ctx) error {
		var (
			err     error
			resp                   = new(RR)
			req                    = new(R)
			address                = fmt.Sprintf("etcd:///resolver/%s/grpc/%s/%s", project, moduleType, moduleName)
			brf     BindReqFunc[R] = DefaultBindReqFunc[R]
		)

		if len(bindReqFn) > 0 && bindReqFn[0] != nil {
			brf = bindReqFn[0]
		}

		if err = brf(c, req); err != nil {
			return err
		}

		// ============================== log ==============================
		if req != nil {
			logrus.Debugf("[assemblies] before do req: %+v", *req)
		} else {
			logrus.Warn("[assemblies] before do req is nil")
		}
		// =================================================================

		resp, err = grpcPool.DoRequest[R, RR](address, service, method, ctx, req, grpc.WithResolvers(resolver), grpc.WithTransportCredentials(insecure.NewCredentials()))

		r, ok := any(resp).(gRPCResp[D])
		if !ok {
			logrus.Errorf("convert resp to gRPCResp err, resp: %T", resp)
			return internal.Resp500(c)
		}

		return internal.Resp(c, r.GetStatus(), r.GetData(), r.GetMsg())
	}

}
